//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_WIN_ATOMIC_H
#define _UXS_WIN_ATOMIC_H

#pragma once

#include "OSWindows.h"

#ifdef PLATFORM_WINDOWS


namespace UX_STL
{
namespace _platform_
{
	using namespace UXTypes;
	using namespace winapi;

	
	//
	// Atomic Operations
	//

	struct AtomicOp
	{
		// type cast //
#		define type_cast( val )	  & ReferenceCast< volatile TypeInfo<T>::Similar_Signed,	volatile T >( val )
#		define itype_cast( val )  & ReferenceCast< volatile TypeInfo<T>::Similar_Int,		volatile T >( val )
#		define ctype_cast( val )	ReferenceCast< const    TypeInfo<T>::Similar_Int,		const    T >( val )
#		define rtype_cast( val )	ReferenceCast< const T, const TypeInfo<T>::Similar_Int >( val )


		// T //
		template <typename T>
		static T  Inc (volatile T & left) {
			return Inc( type_cast( left ) );
		}

		template <typename T>
		static T  Dec (volatile T & left) {
			return Dec( type_cast( left ) );
		}

		template <typename T>
		static T  Add (volatile T & left, const T& right) {
			return Add( type_cast( left ), ctype_cast( right ) );
		}

		template <typename T>
		static T  Sub (volatile T & left, const T& right) {
			return Sub( type_cast( left ), ctype_cast( right ) );
		}

		template <typename T>
		static T  Set (volatile T & left, const T& right) {
			return rtype_cast( Set( itype_cast( left ), ctype_cast( right ) ) );
		}

		template <typename T>
		static T  CmpEx (volatile T & left, const T& right, const T& cmp) {
			return rtype_cast( CmpEx( itype_cast( left ), ctype_cast( right ), ctype_cast( cmp ) ) );
		}

		template <typename T>
		static T  Or (volatile T & left, const T& right) {
			return Or( type_cast( left ), ctype_cast( right ) );
		}

		template <typename T>
		static T  Xor (volatile T & left, const T& right) {
			return Xor( type_cast( left ), ctype_cast( right ) );
		}

		template <typename T>
		static T  And (volatile T & left, const T& right) {
			return And( type_cast( left ), ctype_cast( right ) );
		}
		
		template <typename T>
		static T Get (volatile T const & left) {
			// TODO: memory barrir for non x86 processors 
			return left;
		}

#		undef  type_cast
#		undef  itype_cast
#		undef  ctype_cast
#		undef  rtype_cast


		// int8 //
		static int8  Inc (volatile int8 * ptr)							{ return Add( ptr, 1 ); }
		static int8  Dec (volatile int8 * ptr)							{ return Add( ptr, -1 ); }
		static int8  Add (volatile int8 * ptr, int8 val)				{ return _intrin_::_InterlockedExchangeAdd8( (volatile char *)ptr, val ); }
		static int8  Sub (volatile int8 * ptr, int8 val)				{ return _intrin_::_InterlockedExchangeAdd8( (volatile char *)ptr, -val ); }
		static int8  Set (volatile int8 * ptr, int8 val)				{ return _intrin_::_InterlockedExchange8( (volatile char *)ptr, val ); }
		static int8  CmpEx (volatile int8 * ptr, int8 val, int8 cmp)	{ return _intrin_::_InterlockedCompareExchange8( (volatile char *)ptr, val, cmp ); }
		static int8  Or (volatile int8 * ptr, int8 val)					{ return _intrin_::_InterlockedOr8( (volatile char *)ptr, val ); }
		static int8  Xor (volatile int8 * ptr, int8 val)				{ return _intrin_::_InterlockedXor8( (volatile char *)ptr, val ); }
		static int8  And (volatile int8 * ptr, int8 val)				{ return _intrin_::_InterlockedAnd8( (volatile char *)ptr, val ); }
		
		// int16 //
		static int16 Inc (volatile int16 * ptr)							{ return _intrin_::_InterlockedIncrement16( (volatile short *)ptr ); }
		static int16 Dec (volatile int16 * ptr)							{ return _intrin_::_InterlockedDecrement16( (volatile short *)ptr ); }
		static int16 Add (volatile int16 * ptr, int16 val)				{ return _intrin_::_InterlockedExchangeAdd16( (volatile short *)ptr, val ); }
		static int16 Sub (volatile int16 * ptr, int16 val)				{ return _intrin_::_InterlockedExchangeAdd16( (volatile short *)ptr, -val ); }
		static int16 Set (volatile int16 * ptr, int16 val)				{ return _intrin_::_InterlockedExchange16( (volatile short *)ptr, val ); }
		static int16 CmpEx (volatile int16 * ptr, int16 val, int16 cmp)	{ return _intrin_::_InterlockedCompareExchange16( (volatile short *)ptr, val, cmp ); }
		static int16 Or (volatile int16 * ptr, int16 val)				{ return _intrin_::_InterlockedOr16( (volatile short *)ptr, val ); }
		static int16 Xor (volatile int16 * ptr, int16 val)				{ return _intrin_::_InterlockedXor16( (volatile short *)ptr, val ); }
		static int16 And (volatile int16 * ptr, int16 val)				{ return _intrin_::_InterlockedAnd16( (volatile short *)ptr, val ); }

		// int32 //
		static int32 Inc (volatile int32 * ptr)							{ return _intrin_::_InterlockedIncrement( (volatile long *)ptr ); }
		static int32 Dec (volatile int32 * ptr)							{ return _intrin_::_InterlockedDecrement( (volatile long *)ptr ); }
		static int32 Add (volatile int32 * ptr, int32 val)				{ return _intrin_::_InterlockedExchangeAdd( (volatile long *)ptr, val ); }
		static int32 Sub (volatile int32 * ptr, int32 val)				{ return _intrin_::_InterlockedExchangeAdd( (volatile long *)ptr, -val ); }
		static int32 Set (volatile int32 * ptr, int32 val)				{ return _intrin_::_InterlockedExchange( (volatile long *)ptr, val ); }
		static int32 CmpEx (volatile int32 * ptr, int32 val, int32 cmp)	{ return _intrin_::_InterlockedCompareExchange( (volatile long *)ptr, val, cmp ); }
		static int32 Or (volatile int32 * ptr, int32 val)				{ return _intrin_::_InterlockedOr( (volatile long *)ptr, val ); }
		static int32 Xor (volatile int32 * ptr, int32 val)				{ return _intrin_::_InterlockedXor( (volatile long *)ptr, val ); }
		static int32 And (volatile int32 * ptr, int32 val)				{ return _intrin_::_InterlockedAnd( (volatile long *)ptr, val ); }
		

#	if defined PLATFORM_32BIT
		
		// pointers //
		static void * SetPtr (volatile void ** ptr, void * val)			{ return (void *)_intrin_::_InterlockedExchange( (volatile long *)ptr, (long)val ); }
		static void * CmpExP (volatile void **ptr, void *val, void *cmp){ return (void *)_intrin_::_InterlockedCompareExchange( (volatile long *)ptr, (long)val, (long)cmp ); }

		// int64 //
		static int64 Inc (volatile int64 * ptr)							{ return InterlockedIncrement64( ptr ); }
		static int64 Dec (volatile int64 * ptr)							{ return InterlockedDecrement64( ptr ); }
		static int64 Add (volatile int64 * ptr, int64 val)				{ return InterlockedExchangeAdd64( ptr, val ); }
		static int64 Sub (volatile int64 * ptr, int64 val)				{ return InterlockedExchangeAdd64( ptr, -val ); }
		static int64 Set (volatile int64 * ptr, int64 val)				{ return InterlockedExchange64( ptr, val ); }
		static int64 CmpEx (volatile int64 * ptr, int64 val, int64 cmp)	{ return InterlockedCompareExchange64( ptr, val, cmp ); }
		static int64 Or (volatile int64 * ptr, int64 val)				{ return InterlockedOr64( ptr, val ); }
		static int64 Xor (volatile int64 * ptr, int64 val)				{ return InterlockedXor64( ptr, val ); }
		static int64 And (volatile int64 * ptr, int64 val)				{ return InterlockedAnd64( ptr, val ); }

#	endif	// PLATFORM_32BIT

#	ifdef PLATFORM_64BIT

		// pointers //
		static void * SetPtr (void* volatile* ptr, void * val)			{ return _intrin_::_InterlockedExchangePointer( ptr, val ); }
		static void * CmpExP (void* volatile* ptr, void *val, void *cmp){ return _intrin_::_InterlockedCompareExchangePointer( ptr, val, cmp ); }
		
		// int64 //
		static int64 Inc (volatile int64 * ptr)							{ return _intrin_::_InterlockedIncrement64( ptr ); }
		static int64 Dec (volatile int64 * ptr)							{ return _intrin_::_InterlockedDecrement64( ptr ); }
		static int64 Add (volatile int64 * ptr, int64 val)				{ return _intrin_::_InterlockedExchangeAdd64( ptr, val ); }
		static int64 Sub (volatile int64 * ptr, int64 val)				{ return _intrin_::_InterlockedExchangeAdd64( ptr, -val ); }
		static int64 Set (volatile int64 * ptr, int64 val)				{ return _intrin_::_InterlockedExchange64( ptr, val ); }
		static int64 CmpEx (volatile int64 * ptr, int64 val, int64 cmp)	{ return _intrin_::_InterlockedCompareExchange64( ptr, val, cmp ); }
		static int64 Or (volatile int64 * ptr, int64 val)				{ return _intrin_::_InterlockedOr64( ptr, val ); }
		static int64 Xor (volatile int64 * ptr, int64 val)				{ return _intrin_::_InterlockedXor64( ptr, val ); }
		static int64 And (volatile int64 * ptr, int64 val)				{ return _intrin_::_InterlockedAnd64( ptr, val ); }

#	endif	// PLATFORM_64BIT

	};

}	// _platform_
}	// UX_STL

#endif	// PLATFORM_WINDOWS
#endif	// _UXS_WIN_ATOMIC_H